<!DOCTYPE HTML>
<html lang="en">
  <head>
    <meta charset="utf-8">
    <title>lab</title>
    <link rel="icon" type="image/png" href="favicon.png" />
    <link href="../inter.css" rel="stylesheet">
    <script type="text/javascript">

// dark color scheme by default?
let defaultColorSceme = "light"
if (window.matchMedia('(prefers-color-scheme: dark)').matches) {
  defaultColorSceme = "dark"
  if (document.location.search.indexOf("invert-colors") == -1) {
    document.documentElement.classList.add("color-scheme-dark")
  }
}

    </script>
    <script type="text/javascript" src="samples.js?v=4.0"></script>
    <script type="text/javascript" src="build-version.js?v=4.0"></script>
    <script type="text/javascript" src="font-files.js?v=4.0"></script>
    <link href="lab.css" rel="stylesheet">
    <link rel="stylesheet" type="text/css" href="https://fonts.googleapis.com/css?family=Roboto:400,400i,500,500i,700,700i,900,900i&subset=cyrillic,cyrillic-ext,greek,greek-ext,latin-ext,vietnamese">
  </head>
  <body class="init-anim varfont">
    <div id="sidebar-button"></div>
    <div class="options">

      <div class="flex-x">
        <label title="Use variable font instead of static font files">
          <span>VF</span>
          <input type="checkbox" name="varfont" checked>
        </label>
        <label class="italic-setting" title="Italic">
          <span>I</span>
          <input type="checkbox" name="italic">
        </label>
        <label title="Swap color scheme">
          <b>☀</b>
          <input type="checkbox" name="invert-colors">
        </label>
        <label title="Draw background behind samples">
          <b>■</b>
          <input type="checkbox" name="draw-sample-bg">
        </label>
      </div>

      <label class="label-and-value with-slider">
        <span>Size:</span>
        <input type="number" value="22" step="1" min="6" max="1024" name="size">
        <input type="range" value="22" step="1" min="6" max="256" name="_sizeRange">
        <!-- <note><span class="unit">dp</span></note> -->
      </label>

      <!-- Variable font controls (hidden when not using variable fonts) -->

      <label class="label-and-value with-slider varfontControl">
        <span title="Optical size">opsz:</span>
        <input type="number" value="0" step="1" min="14" max="32" name="varOpszNum">
        <input type="range" value="0" step="0.01" min="14" max="32" name="varOpsz">
      </label>

      <label class="label-and-value with-slider varfontControl">
        <span title="Weight">wght:</span>
        <input type="number" value="400" step="1" min="100" max="900" name="varWeightNum">
        <input type="range" value="400" step="1" min="100" max="900" name="varWeight">
      </label>

      <label class="label-and-value staticfontControl">
        <span>Weight:</span>
        <select name="weight" style="max-width:100px">
          <option value="100">Thin (100)</option>
          <option value="200">Extra Light (200)</option>
          <option value="300">Light (300)</option>
          <option value="400" selected>Regular (400)</option>
          <option value="500">Medium (500)</option>
          <option value="600">Semi Bold (600)</option>
          <option value="700">Bold (700)</option>
          <option value="800">Extra Bold (800)</option>
          <option value="900">Black (900)</option>
        </select>
      </label>

      <label class="label-and-value">
        <span>Family:</span>
        <select name="family">
          <option value="text" selected>Inter</option>
          <option value="display">Inter Display</option>
        </select>
      </label>

      <label class="label-and-value">
        <span>Sample text:</span>
        <select name="sample"></select>
      </label>

      <div class="checkbox-group repertoireControl">
        <label class="label-and-value">
          <span>Repertoire order:</span>
          <select name="repertoireOrder">
            <option value="" selected>Original</option>
            <option value="u">Unicode</option>
          </select>
        </label>
      </div>

      <label class="label-and-value">
        <span>Letter spacing:</span>
        <input type="number" value="" placeholder="" step="0.1" name="letterSpacing">
        <note><span class="unit">%</span><div class="img-button reset reset-letter-spacing" tabindex="0"></div>
        </note>
      </label>

      <label class="label-and-value">
        <span>Line height:</span>
        <input type="number" value="" placeholder="" step="1" min="0" max="1000" name="lineHeight">
        <note><span class="unit">dp</span><div class="img-button reset reset-line-height" tabindex="0"></div>
        </note>
      </label>

      <label class="label-and-value">
      <span>Transform:</span>
      <select name="text-transform">
        <option value="none" selected>none</option>
        <option value="capitalize">Capitalize</option>
        <option value="uppercase">UPPERCASE</option>
        <option value="lowercase">lowercase</option>
      </select></label>

      <label class="label-and-value">
      <span>Decoration:</span>
      <select name="text-decoration">
        <option value="none" selected>none</option>
        <option value="underline" style="text-decoration:underline">underline</option>
        <option value="overline">overline</option>
        <option value="underline overline">underline &amp; overline</option>
        <option value="line-through">line-through</option>
        <option value="wavy underline">wavy underline</option>
      </select></label>

      <!-- <h3>Display</h3> -->

      <label class="label-and-value">
      <span>Anti-alias:</span>
      <select name="antialias">
        <option value="greyscale" selected>Greyscale</option>
        <option value="subpixel">Subpixel</option>
        <option value="default">Browser default</option>
      </select>
      </label>

      <label class="label-and-value">
      <span>Compare:</span>
      <select name="compare">
        <option value="-" selected>Nothing</option>
        <!-- <option value="inter-other">Other Inter family</option> -->
        <option value="system">System font</option>
      </select>
      </label>


      <h3>Features</h3>

      <div class="checkbox-group">
      <!-- case --><label title="Case-Sensitive Forms"><input type="checkbox" class="featopt" name="feat:case"><span>case &nbsp;(Case-Sensitive Forms)</span></label>
      <!-- dlig --><label title="Discretionary ligatures"><input type="checkbox" class="featopt" name="feat:dlig"><span>dlig &nbsp;(Discretionary ligatures)</span></label>
      <!-- frac --><label title="Contextual automatic fractions"><input type="checkbox" class="featopt" name="feat:frac"><span>frac &nbsp;(Auto fractions)</span></label>
      <!-- dnom --><label title="Convert all numbers to denominators"><input type="checkbox" class="featopt" name="feat:dnom"><span>dnom &nbsp;(Denominators)</span></label>
      <!-- numr --><label title="Convert all numbers to numerators"><input type="checkbox" class="featopt" name="feat:numr"><span>numr &nbsp;(Numerators)</span></label>
      <!-- subs --><label title="Subscript"><input type="checkbox" class="featopt" name="feat:subs"><span>subs &nbsp;(Subscript)</span></label>
      <!-- sups --><label title="Superscript"><input type="checkbox" class="featopt" name="feat:sups"><span>sups &nbsp;(Superscript)</span></label>
      <!-- tnum --><label title="Tabular numbers (fixed width)"><input type="checkbox" class="featopt" name="feat:tnum"><span>tnum &nbsp;(Tabular numbers)</span></label>
      <!-- zero --><label title="Slashed zero"><input type="checkbox" class="featopt" name="feat:zero"><span>zero &nbsp;(Slashed zero)</span></label>
      </div>

      <div class="checkbox-group">
      <span>Stylistic sets:</span>
      <!-- ss01 --><label title='Stylistic set 1 "Open Digits"'><input type="checkbox" class="featopt" name="feat:ss01"><span>ss01 &nbsp;(Open Digits)</span></label>
      <!-- ss02 --><label title='Stylistic set 2 "Disambiguation (with zero)"'><input type="checkbox" class="featopt" name="feat:ss02"><span>ss02 &nbsp;(Disambiguation w/ zero)</span></label>
      <!-- ss03 --><label title='Stylistic set 3 "Round quotes &amp; commas"'><input type="checkbox" class="featopt" name="feat:ss03"><span>ss03 &nbsp;(Round quotes &amp; commas)</span></label>
      <!-- ss04 --><label title='Stylistic set 4 "Disambiguation without slashed zero"'><input type="checkbox" class="featopt" name="feat:ss04"><span>ss04 &nbsp;(Disambiguation w/o zero)</span></label>
      <!-- ss05 --><label title='Stylistic set 5 "Circled characters"'><input type="checkbox" class="featopt" name="feat:ss05"><span>ss05 &nbsp;(Circled characters)</span></label>
      <!-- ss06 --><label title='Stylistic set 6 "Squared characters"'><input type="checkbox" class="featopt" name="feat:ss06"><span>ss06 &nbsp;(Squared characters)</span></label>
      <!-- ss07 --><label title='Stylistic set 7 "Square punctuation"'><input type="checkbox" class="featopt" name="feat:ss07"><span>ss07 &nbsp;(Square punctuation)</span></label>
      <!-- ss08 --><label title='Stylistic set 8 "Square quotes"'><input type="checkbox" class="featopt" name="feat:ss08"><span>ss08 &nbsp;(Square quotes)</span></label>
      </div>

      <div class="checkbox-group">
      <span>Character variants:</span>
      <!-- cv01 --><label title='Character Variant 1 "Alternate one"'><input type="checkbox" class="featopt" name="feat:cv01"><span>cv01 &nbsp;(Alternate one)</span></label>
      <!-- cv02 --><label title='Character Variant 2 "Open four"'><input type="checkbox" class="featopt" name="feat:cv02"><span>cv02 &nbsp;(Open four)</span></label>
      <!-- cv03 --><label title='Character Variant 3 "Open six"'><input type="checkbox" class="featopt" name="feat:cv03"><span>cv03 &nbsp;(Open six)</span></label>
      <!-- cv04 --><label title='Character Variant 4 "Open nine"'><input type="checkbox" class="featopt" name="feat:cv04"><span>cv04 &nbsp;(Open nine)</span></label>
      <!-- cv05 --><label title='Character Variant 5 "Lower case L with tail")'><input type="checkbox" class="featopt" name="feat:cv05"><span>cv05 &nbsp;(Lower case L with tail)</span></label>
      <!-- cv06 --><label title='Character Variant 6 "Simplified u")'><input type="checkbox" class="featopt" name="feat:cv06"><span>cv06 &nbsp;(Simplified u)</span></label>
      <!-- cv07 --><label title='Character Variant 7 "Alternate German double-s")'><input type="checkbox" class="featopt" name="feat:cv07"><span>cv07 &nbsp;(German double-s)</span></label>
      <!-- cv08 --><label title='Character Variant 8 "Upper-case i with serif")'><input type="checkbox" class="featopt" name="feat:cv08"><span>cv08 &nbsp;(Upper-case i with serif)</span></label>
      <!-- cv09 --><label title='Character Variant 9 "Flat top three")'><input type="checkbox" class="featopt" name="feat:cv09"><span>cv09 &nbsp;(Flat top three)</span></label>
      <!-- cv10 --><label title='Character Variant 10 "Capital G with spur")'><input type="checkbox" class="featopt" name="feat:cv10"><span>cv10 &nbsp;(Capital G with spur)</span></label>
      <!-- cv11 --><label title='Character Variant 11 "Single-storey a")'><input type="checkbox" class="featopt" name="feat:cv11"><span>cv11 &nbsp;(Single-storey a)</span></label>
      <!-- cv12 --><label title='Character Variant 12 "Compact f")'><input type="checkbox" class="featopt" name="feat:cv12"><span>cv12 &nbsp;(Compact f)</span></label>
      <!-- cv13 --><label title='Character Variant 13 "Compact t")'><input type="checkbox" class="featopt" name="feat:cv13"><span>cv13 &nbsp;(Compact t)</span></label>
      </div>

      <div class="checkbox-group">
      <span>Partial or uncommon:</span>
      <!-- cpsp --><label title='Capital spacing (adds 16 UPM to each sidebearing)'><input type="checkbox" class="featopt" name="feat:cpsp"><span>cpsp &nbsp;(Capital spacing)</span></label>
      <!-- c2sc --><label title="Small Capitals From Capitals"><input type="checkbox" class="featopt" name="feat:c2sc"><span>c2sc &nbsp;(Small Caps; incomplete)</span></label>
      <!-- salt --><label title='Stylistic Alternates'><input type="checkbox" class="featopt" name="feat:salt"><span>salt &nbsp;(Stylistic Alternates)</span></label>
      <!-- aalt --><label title='Access All Alternates'><input type="checkbox" class="featopt" name="feat:aalt"><span>aalt &nbsp;(Access All Alternates)</span></label>
      </div>

      <div class="checkbox-group">
      <span>Enabled by default:</span>
      <!-- calt --><label title="Contextual alternates"><input type="checkbox" class="featopt" name="feat:calt=0"> Disable calt &nbsp;(Contextual alternates)</label>
      <!-- ccmp --><label title="Glyph Composition/Decomposition"><input type="checkbox" class="featopt" name="feat:ccmp=0"> Disable ccmp</label>
      <!-- locl --><label title='Localized Forms'><input type="checkbox" class="featopt" name="feat:locl=0"><span>Disable locl &nbsp;(Localized Forms)</span></label>
      <!-- kern --><label title="Kerning"><input type="checkbox" class="featopt" name="feat:kern=0"> Disable kern &nbsp;(Kerning)</label>
      </div>

    </div>

    <boxes>
      <box contenteditable spellcheck="false" class="primaryFont "><span>Rectangle</span></box>
      <box contenteditable spellcheck="false" class="primaryFont positive"><span>Rectangle</span></box>
      <box contenteditable spellcheck="false" class="primaryFont positive tight"><span>Rectangle</span></box>
      <box contenteditable spellcheck="false" class="primaryFont centered positive"><span>Rectangle</span></box>

      <sep></sep>

      <box contenteditable spellcheck="false" class="secondaryFont showOnlyWithSecondarySample"><span>Rectangle</span></box>
      <box contenteditable spellcheck="false" class="secondaryFont positive showOnlyWithSecondarySample"><span>Rectangle</span></box>
      <box contenteditable spellcheck="false" class="secondaryFont positive tight showOnlyWithSecondarySample"><span>Rectangle</span></box>
      <box contenteditable spellcheck="false" class="secondaryFont centered positive showOnlyWithSecondarySample"><span>Rectangle</span></box>
    </boxes>

    <div class="preview">
      <samples>
        <sample contenteditable spellcheck="false" class="primary inter"></sample>
        <sample contenteditable spellcheck="false" class="secondary"></sample>
      </samples>
    </div>

    <div id="measure" class="inter">Åj</div>
  </body>
  <script type="text/javascript">(function(){

function InterDynamicTracking(fontSize, family /* :"text"|"display" */) {
  var a = -0.0223, b = 0.185, c = -0.1745;
  if (family == "display") {
    a = -0.015; b = 0.17; c = -0.12
  }
  // tracking = a + b * e ^ (c * fontSize)
  return a + b * Math.pow(Math.E, c * fontSize)
}

document.body.style.fontFamily = fontFamilyName


function parseQueryString(qs) {
  return new Map(
    qs.replace(/^\?+/g,'')
      .split('&')
      .filter(s => s.trim())
      .map(s => s.split('=').map(decodeURIComponent))
  )
}


function fmtQueryString(mapLikeIterable) {
  let pairs = []
  for (let kv of mapLikeIterable) {
    let k = kv[0]
    let v = kv[1]
    if (v === undefined) {
      pairs.push(encodeURIComponent(k))
    } else {
      v = (v === true) ? 1 : (v === false || v === null) ? 0 : v
      pairs.push(encodeURIComponent(k) + '=' + encodeURIComponent(v))
    }
  }
  return pairs.sort().join('&')
}


function setCurrentQueryString(queryString) {
  replaceURL(() => {
    if (typeof queryString == "function") {
      queryString = queryString()
    }
    return document.location.pathname + (queryString ? "?" + queryString : "")
  })
}


function setCurrentQueryStringParam(key, value, zeroValue) {
  setCurrentQueryString(() => {
    let qs = parseQueryString(document.location.search)
    if (value === undefined || value === zeroValue) {
      qs.delete(key)
    } else {
      qs.set(key, value)
    }
    return fmtQueryString(qs)
  })
}


// replaceURL(url :string|Function, title? :string, meta? :any)
var replaceURL = (()=>{
  // We employ some backoff on calling history.replaceState as
  // Chrome will start to throttle (meaning ignoring) calls to
  // history.replaceState if the frequency gets too high.
  // Safari 13 provides a budget of 200 replaceState calls in a 30 second sliding time window.
  // Chrome instead uses an IPC message flooding prevention (https://crbug.com/882238)

  let timer = null
  let queued = null //  {url, title, meta} when queued, null when not
  let isRetrying = false

  function flush() {
    clearTimeout(timer)
    timer = null
    if (queued) {
      let url = typeof queued.url == "function" ? queued.url() : queued.url
      try {
        history.replaceState(queued.meta, queued.title, url)
        queued = null
        isRetrying = false
      } catch (err) {
        if (!isRetrying) {
          console.warn(String(err))
        }
        // back off
        isRetrying = true
        timer = setTimeout(flush, 1000)
      }
    }
  }

  return function replaceURL(url, title, meta) {
    queued = { url, title, meta }
    if (timer === null) {
      flush()
      timer = setTimeout(flush, 200)
    }
  }
})()


class BoundVar {
  constructor(name, e, valueGetter, valueSetter, parentVars) {
    this.name = name
    this.e = e
    this.valueGetter = valueGetter
    this.valueSetter = valueSetter
    this.isCheckbox = e.type == 'checkbox'
    this.isNumber = e.type == 'number' || e.type == 'range'
    this.lastValue = this.getValue()
    this.parentVars = parentVars
    this.defaultValue = this.lastValue
    this._changeCallbacks = []
    this._isInitialSetValue = true
  }

  refreshValue(ev) {
    let value = this.getValue(ev)
    this.setValue(value)
    return value
  }

  getValue(ev) {
    return this.valueGetter ? this.valueGetter(this.e, this.lastValue, ev)
         : this.isCheckbox ? this.e.checked
         : this.isNumber ? this.e.valueAsNumber
         : this.e.value
  }

  onChange(callback) {
    this._changeCallbacks.push(callback)
  }

  removeChangeListener(callback) {
    let i = 0
    for (; i < this._changeCallbacks.length; i++) {
      if (this._changeCallbacks[i] === callback) {
        this._changeCallbacks.splice(i, 1)
        return true
      }
    }
    return false
  }

  setValue(value) {
    if (this.isCheckbox && typeof value != 'boolean') {
      value = parseInt(value)
      if (isNaN(value)) {
        value = 0
      }
    }

    if (this.valueSetter) {
      let isInitial = this._isInitialSetValue
      this._isInitialSetValue = false
      const v = this.valueSetter(this.e, value, isInitial)
      if (v !== undefined) {
        value = v
      }
    }

    if (this.isCheckbox) {
      if ((this.e.checked = !!value)) {
        this.e.setAttribute("checked", "")
      } else {
        this.e.removeAttribute("checked")
      }
    } else if (this.isNumber && typeof value == 'number' && 'valueAsNumber' in this.e) {
      if (isNaN(value)) {
        this.e.value = null
      } else if (this.e.valueAsNumber != value) {
        this.e.valueAsNumber = value
      }
    } else if (this.e.value != value) {
      this.e.value = value
    }

    this.lastValue = value

    for (let f of this._changeCallbacks) {
      f(value, this)
    }
  }
}

class Vars {
  constructor(queryString) {
    this.values = parseQueryString(queryString)
    this.vars = new Map()
    // this._historyReplaceStateTimer = null
    // this._needsHistoryReplaceState = false
  }

  getValue(name) {
    return this.values.get(name)
  }

  setValue(name, value) {
    let v = this.vars.get(name)
    if (!v) {
      return null
    }
    v.setValue(value)
    this._storeValue(name, value)
    return value
  }

  _storeValue(name, value) {
    // if (this.values.get(name) === value) {
    //   return
    // }
    if (value === null || value === undefined) {
      this.values.delete(name)
    } else {
      this.values.set(name, value)
    }
    setCurrentQueryString(() => this.getQueryString())
  }

  // _performHistoryReplaceState() {
  //   // let qs = mergeQueryString(document.location.search, this.getQueryString())
  //   setCurrentQueryString(this.getQueryString())
  //   this._needsHistoryReplaceState = false
  // }

  // setNeedsHistoryReplaceState() {
  //   // We employ some backoff on calling history.replaceState as
  //   // Chrome will start to throttle (meaning ignoring) calls to
  //   // history.replaceState if the frequency gets too high.
  //   // Safari 13 provides a budget of 200 replaceState calls in a 30 second sliding time window.
  //   // Chrome instead uses an IPC message flooding prevention (https://crbug.com/882238)
  //   if (this._historyReplaceStateTimer === null) {
  //     this._performHistoryReplaceState()
  //     this._historyReplaceStateTimer = setTimeout(() => {
  //       this._historyReplaceStateTimer = null
  //       if (this._needsHistoryReplaceState) {
  //         this._performHistoryReplaceState()
  //       }
  //     }, 200)
  //   } else {
  //     this._needsHistoryReplaceState = true
  //   }
  // }

  refreshValue(name) {
    let v = this.vars.get(name)
    return v === undefined || v === null ? null : this._refreshValue(v)
  }

  _refreshValue(v, ev) {
    let value = v.refreshValue(ev)
    this._storeValue(v.name, value)
    return value
  }

  getQueryString() {
    let qs = parseQueryString(document.location.search)
    // patch qs: set or remove any attributes that are registered in this.values
    for (let e of this.vars) {
      let k = e[0], vr = e[1], v = this.values.get(k)
      if (v === undefined || vr.defaultValue == v || k[0] == '_') {
        qs.delete(k)
      } else {
        qs.set(k, v)
      }
    }
    return fmtQueryString(qs)
  }

  // bind(name :string,
  //      sel :Element|string,
  //      valueSetter? :(e:Element, value:any, isInitial:bool)=>void,
  //      valueGetter? :(e:Element)=>any)
  // bind(name :string,
  //      valueSetter? :(e:Element, value:any, isInitial:bool)=>void,
  //      valueGetter? :(e:Element, prevValue:any, ev?:Event)=>any)
  bind(name, sel, valueSetter, valueGetter) {
    if (typeof sel == 'function' || sel === undefined) {
      valueGetter = valueSetter
      valueSetter = sel
      sel = '[name="'+name+'"]'
    }
    let e = typeof sel == 'string' ? document.querySelector(sel) : sel;
    let v = new BoundVar(name, e, valueGetter, valueSetter, this)
    this.vars.set(name, v)

    if (v.isNumber) {
      // SHIFT-ARROW = 10 increments
      // SHIFT-ALT-ARROW = x2 increments
      e.addEventListener('keydown', ev => {
        if (!ev.shiftKey) {
          return
        }
        switch (ev.key) {
          case 'ArrowRight':
          case 'ArrowUp': {
            if (ev.key == 'ArrowRight' && e.type != "range") {
              return
            }
            if (ev.altKey) {
              ev.target.valueAsNumber *= 2
            } else {
              ev.target.valueAsNumber += 10
            }
            ev.preventDefault()
            ev.stopPropagation()
            this._refreshValue(v, ev)
            break
          }
          case 'ArrowLeft':
          case 'ArrowDown': {
            if (ev.key == 'ArrowLeft' && e.type != "range") {
              return
            }
            if (ev.altKey) {
              ev.target.valueAsNumber /= 2
            } else {
              ev.target.valueAsNumber -= 10
            }
            ev.preventDefault()
            ev.stopPropagation()
            this._refreshValue(v, ev)
            break
          }
        }
      }, {capture:true, passive:false})
    }

    let onChange = ev => this._refreshValue(v, ev)
    e.addEventListener('input', onChange)
    if (v.isCheckbox) {
      e.addEventListener('change', onChange)
    }

    let existingValue = this.values.get(name)
    if (existingValue !== null && existingValue !== undefined) {
      if (v.isNumber) {
        existingValue = parseFloat(existingValue)
      } else if (v.isCheckbox) {
        existingValue = existingValue != '0' &&
                        existingValue != 'false' &&
                        existingValue != 'off'
      }
      v.setValue(existingValue)
    } else {
      onChange(null)
    }

    return v
  }
}


// function mergeQueryString(a, b) {
//   a = a.replace(/^[\?\&]+/, "").split('&').sort().map(v => v.split('='))
//   b = b.replace(/^[\?\&]+/, "").split('&').sort().map(v => v.split('='))
//   let s = new Map(a)
//   for (let kv of b) {
//     s.set(kv[0], kv[1])
//   }
//   return Array.from(s).map(kv => kv[1] ? kv.join('=') : kv[0]).join('&')
// }



function main() {
  const vars = new Vars(document.location.search)

  const $  = (q, el) => (el || document).querySelector(q)
  const $$ = (q, el) => [].slice.call((el || document).querySelectorAll(q))

  let interUISample = $('sample.inter');
  let secondarySample = $('sample.secondary');
  secondarySample.innerText = interUISample.innerText;

  const measureDiv = $('#measure')

  const secondaryFontElements = $$('.secondaryFont')
  const primaryFontElements = $$('.primaryFont')

  const repertoireControl = $('.repertoireControl')
  const samplesElement = $('samples')
  const boxes = $('boxes')

  let keyPressed = ""
  let shiftKeyPressed = false
  const checkKeys = ev => {
    keyPressed = ev.key
    shiftKeyPressed = ev.shiftKey
  }
  window.addEventListener('keydown', checkKeys)
  window.addEventListener('keyup', checkKeys)


  // sidebar show/hide
  const sidebarButton = $("#sidebar-button")
  let sidebarMinimized = false
  function setSidebarMinimized(minimized) {
    sidebarMinimized = minimized
    document.body.classList.toggle("sidebar-minimized", sidebarMinimized)
    setCurrentQueryStringParam("nosidebar", sidebarMinimized ? 1 : undefined)
    sidebarButton.title = sidebarMinimized ? "Show controls" : "Hide controls"
  }
  sidebarButton.onclick = () => setSidebarMinimized(!sidebarMinimized)
  setSidebarMinimized(document.location.search.indexOf('nosidebar') != -1)
  // setInterval(()=>{ setSidebarMinimized(!sidebarMinimized) },2000)

  // filter paste to match style
  $$('[contenteditable]').forEach(el => {
    el.addEventListener('paste', ev => {
      ev.preventDefault()
      let text = ev.clipboardData.getData("text/plain")
      document.execCommand("insertHTML", false, text)
    }, {capture:true,passive:false})
  })

  // prevent clicks on img-button from changing focus
  $$('.img-button').forEach(el => {
    el.addEventListener('pointerdown', ev => {
      ev.preventDefault()
    }, {passive:false, capture:true})
  })

  function forEachGlyphlist(fn) {
    let elements = samplesElement.querySelectorAll('.glyphlist')
    if (elements) {
      for (let i = 0; i < elements.length; ++i) {
        fn(elements[i], i)
      }
    }
  }

  function setGlyphlistClass(className, add) {
    forEachGlyphlist(gl => {
      if (add) {
        gl.classList.add(className)
      } else {
        gl.classList.remove(className)
      }
    })
  }


  const lineHeightInput = $('[name="lineHeight"]')

  let measurePending = false
  const measure = () => {
    const r = measureDiv.getBoundingClientRect()
    measurePending = false
    lineHeightInput.placeholder = r.height
    document.documentElement.style.setProperty("--line-height", r.height + "px")
  }

  window.addEventListener('load', measure)

  const cssAffectedElements = [
    interUISample,
    secondarySample,
    measureDiv
  ].concat(secondaryFontElements).concat(primaryFontElements)
  const ignoreStylePropsInBoxes = new Set([
    'line-height'
  ])

  let setCSSProp = (name, value) => {
    if (value === null || value === undefined) {
      cssAffectedElements.forEach(e => e.style.removeProperty(name))
    } else {
      cssAffectedElements.forEach(e => {
        if (e.nodeName != 'BOX' || !ignoreStylePropsInBoxes.has(name)) {
          e.style.setProperty(name, value)
        }
      })
    }
    if (!measurePending) {
      measurePending = true
      window.requestAnimationFrame(measure)
    }
  }

  let setVendorPrefixedCSSProp = (name, value) => {
    setCSSProp(name, value)
    setCSSProp('-webkit-' + name, value)
    setCSSProp('-moz-' + name, value)
    setCSSProp('-ms-' + name, value)
  }


  let feats = new Map()
  let featVars = new Map()
  let updateFeaturesStyleTimer = null

  function updateFeaturesStyle() {
    let css = Array.from(feats).map(f => `"${f[0]}" ${f[1]}`).join(', ')
    setCSSProp('font-feature-settings', css)
  }

  function scheduleUpdateFeaturesStyle() {
    if (updateFeaturesStyleTimer === null) {
      updateFeaturesStyleTimer = setTimeout(() => {
        updateFeaturesStyleTimer = null
        updateFeaturesStyle()
      }, 1)
    }
  }

  function setFeature(feat, val, dontUpdateVar/*=false*/) {
    if (typeof val == 'boolean') {
      val = val ? 1 : 0
    }
    let prevVal = feats.get(feat)
    if (prevVal !== val) {
      feats.set(feat, val)
      scheduleUpdateFeaturesStyle()
      if (!dontUpdateVar) {
        let vr = featVars.get(feat)
        if (vr) {
          vr.setValue(val)
        }
      }
    }
  }

  // sample text
  const samplesSelect = $('select[name="sample"]')
  for (let [k,v] of samples) {
    const opt = document.createElement('option')
    opt.innerText = k
    if (v) {
      opt.value = k
    } else {
      opt.disabled = true
    }
    samplesSelect.appendChild(opt)
  }

  vars.bind('repertoireOrder', (e, v) => {
    let currOrder = repertoireOrder
    if (v == 'u') {
      repertoireOrder = RepertoireOrderUnicode
    } else {
      repertoireOrder = RepertoireOrderGlyphList
    }
    if (sampleVar && currOrder != repertoireOrder) {
      sampleVar.refreshValue(null)
    }
  })

  var sizeVar, varSizeRange
  var varSizeSettingValueImpl = false

  sizeVar = vars.bind('size', (e, v, isInitial) => {
    boxes.style.display = (v > 20) ? 'none' : null
    setCSSProp('font-size', v + 'px')
    document.documentElement.style.setProperty("--font-size", v + "px")
    setGlyphlistClass('hideNames', v < 36)
    if (varSizeRange && !varSizeSettingValueImpl) {
      varSizeSettingValueImpl = true
      varSizeRange.setValue(v)
      varSizeSettingValueImpl = false
    }
    return v
  })
  varSizeRange = vars.bind('_sizeRange', (e, v, isInitial) => {
    if (isInitial) {
      return sizeVar.getValue()
    } else if (!varSizeSettingValueImpl) {
      varSizeSettingValueImpl = true
      vars.setValue("size", v)
      varSizeSettingValueImpl = false
    }
  }, (e, prevValue, ev) => {
    v = e.valueAsNumber
    if (shiftKeyPressed && (keyPressed == "" || keyPressed == "Shift")) {
      v = Math.round(v / 8) * 8
    }
    return v
  })


  let usingVarFont = false
  let usingFontFamily = "text"
  var varWeightRange, varOpszRange
  var varWeightSettingValueImpl = false
  var varOpszSettingValueImpl = false

  function getFontFamily(overrideFamily) {
    return (
      (overrideFamily || usingFontFamily) == "text" ? (
        usingVarFont ? fontFamilyNameVar : fontFamilyName
      ) : (
        usingVarFont ? fontFamilyNameDisplayVar : fontFamilyNameDisplay
      )
    )
  }

  let currentBodyWeightClass = null
  let varWeight = vars.bind('weight', (e, v) => {
    setCSSProp('font-weight', v)
    if (currentBodyWeightClass) {
      document.body.classList.remove(currentBodyWeightClass)
    }
    document.body.classList.add(currentBodyWeightClass = 'font-weight-'+v)
  })

  var italicVar = vars.bind('italic', (e, on) => {
    document.body.classList[on ? 'add' : 'remove']('italic')
  })

  let varState = {
    weight: 400, // 400..900
    opsz: 14,    // 14..32
  }

  function updateVarFont() {
    if (usingVarFont) {
      setCSSProp(
        "font-variation-settings",
        `"wght" ${varState.weight}, "opsz" ${varState.opsz}`
      )
    } else {
      setCSSProp("font-variation-settings", null)
    }
  }

  vars.bind('varfont', (e, on, isInitial) => {
    usingVarFont = on
    document.body.classList.toggle('varfont', on)
    if (on) {
      if (!isInitial) {
        // copy value of static weight to var weight
        let w = parseInt(varWeight.getValue())
        if (!isNaN(w) && varWeightRange && !varWeightSettingValueImpl) {
          varWeightRange.setValue(w)
        }
      }
    } else {
      if (!isInitial && varWeightRange) {
        // copy value of var weight to static weight
        let w = varWeightRange.getValue()
        if (!isNaN(w)) {
          vars.setValue("weight", Math.round(w / 100) * 100)
        }
      }
    }
    document.body.style.fontFamily = getFontFamily()
    updateVarFont()
  })

  let varWeightNum = vars.bind('_wght', '[name="varWeightNum"]', (e, v) => {
    if (varWeightRange && !varWeightSettingValueImpl)
      vars.setValue("wght", v)
  })

  let varOpszNum = vars.bind('_opsz', '[name="varOpszNum"]', (e, v) => {
    if (varOpszRange && !varOpszSettingValueImpl)
      vars.setValue("opsz", v)
  })

  function snapValue(v, factor) {
    if (shiftKeyPressed && (keyPressed == "" || keyPressed == "Shift"))
      v = Math.round(v / factor) * factor
    return v
  }

  varWeightRange = vars.bind('wght', '[name="varWeight"]', (e, v) => {
    varState.weight = v
    varWeightSettingValueImpl = true
    varWeightNum.setValue(v)
    varWeightSettingValueImpl = false
    updateVarFont()
  }, (e, prevValue, ev) => {
    if (prevValue === undefined)
      return 400
    return snapValue(e.valueAsNumber, 100)
  })

  varOpszRange = vars.bind('opsz', '[name="varOpsz"]', (e, v) => {
    varState.opsz = v
    varOpszSettingValueImpl = true
    varOpszNum.setValue(v)
    varOpszSettingValueImpl = false
    updateVarFont()
  }, (e, prevValue, ev) => {
    return snapValue(e.valueAsNumber, 1)
  })


  // compare
  let secondarySampleClassNameAddition = null
  let currentSecondarySampleClassName = null
  function updateSecondarySample() {
    setSecondarySampleClassName(currentSecondarySampleClassName)
  }

  const setSecondarySampleClassName = className => {
    currentSecondarySampleClassName = className
    if (secondarySampleClassNameAddition) {
      secondarySample.style.fontFamily = null
      secondarySample.classList.remove(secondarySampleClassNameAddition)
      secondaryFontElements.forEach(e =>
        e.classList.remove(secondarySampleClassNameAddition))
    }
    if (className) {
      // let explicitFontFamily = null
      // if (className == "inter-other-font") {
      //   let otherFamily = usingFontFamily == "text" ? "display" : "text"
      //   explicitFontFamily = getFontFamily(otherFamily)
      // }
      // secondarySample.style.fontFamily = explicitFontFamily
      secondarySample.classList.add(className)
      secondaryFontElements.forEach(e => e.classList.add(className))
    }
    secondarySampleClassNameAddition = className || null
  }

  const enableSecondarySample = className => {
    setSecondarySampleClassName(className)
    secondarySample.style.display = null
    document.body.classList.remove('secondarySampleDisabled')
  }
  const disableSecondarySample = className => {
    setSecondarySampleClassName(null)
    secondarySample.style.display = 'none'
    document.body.classList.add('secondarySampleDisabled')
  }

  vars.bind('compare', (e, v) => {
    disableSecondarySample()
    switch (v) {
      case 'system': enableSecondarySample('systemFont'); break
      //case 'inter-other': enableSecondarySample('inter-other-font'); break
      default: return '-';
    }
  }, e => (e.value && e.value != '-') ? e.value : null)


  vars.bind('family', (el, family, isInitial) => {
    usingFontFamily = family
    document.body.style.fontFamily = getFontFamily()
    updateSecondarySample()
    if (!isInitial) {
      updateImplicitLetterSpacing()
    }
  })

  let resetLineHeightButton = $('.reset-line-height')
  let resetLetterSpacingButton = $('.reset-letter-spacing')


  function calcImplicitLetterSpacing(size, el) {
    let t = InterDynamicTracking(size, usingFontFamily)
    let v = parseFloat((t * 100).toFixed(1))
    ;(el || letterSpacingVar.e).placeholder = v
    return v
  }

  let letterSpacingVar = vars.bind('letterSpacing', (el, v) => {
    resetLetterSpacingButton.classList.toggle("disabled", v === '')
    if (v !== '') {
      resetLetterSpacingButton.tabIndex = null
    } else {
      resetLetterSpacingButton.tabIndex = -1
    }
    if (v === '')
      v = calcImplicitLetterSpacing(sizeVar.getValue(), el)
    setCSSProp('letter-spacing', (v / 100) + 'em')
  }, (el, prevValue, ev) => {
    if (ev && !ev.inputType && !prevValue) {
      // step increment/decrement
      let delta = el.valueAsNumber == 0 ? -1 : el.valueAsNumber
      return parseFloat(el.placeholder) + delta
    }
    return el.value || ""
  })

  function updateImplicitLetterSpacing() {
    if (!letterSpacingVar.getValue()) {
      v = calcImplicitLetterSpacing(sizeVar.getValue())
      setCSSProp('letter-spacing', (v / 100) + 'em')
    }
  }

  // update implicit letter spacing when size changes
  sizeVar.onChange(size => {
    let t = letterSpacingVar.lastValue
    if (!t || isNaN(t)) {
      updateImplicitLetterSpacing()
    }
  })

  resetLetterSpacingButton.addEventListener('click', ev => {
    vars.setValue('letterSpacing', '')
    ev.stopPropagation()
    ev.preventDefault()
  }, {passive:false,capture:true})


  vars.bind('lineHeight', lineHeightInput, (e, v) => {
    setCSSProp('line-height', v ? v + 'px' : null)
    resetLineHeightButton.classList.toggle("disabled", !v)
    if (v) {
      resetLineHeightButton.tabIndex = null
    } else {
      resetLineHeightButton.tabIndex = -1
    }
  }, (e, prevValue, ev) => {
    if (ev && !ev.inputType && !prevValue) {
      // step increment/decrement
      let delta = e.valueAsNumber == 0 ? -1 : e.valueAsNumber
      return parseFloat(e.placeholder) + delta
    }
    if (e.valueAsNumber < 0) {
      return Math.abs(e.valueAsNumber)
    }
    return e.value || ""
  })

  resetLineHeightButton.addEventListener('click', ev => {
    vars.setValue('lineHeight', '')
    ev.stopPropagation()
    ev.preventDefault()
  }, {passive:false,capture:true})

  vars.bind("invert-colors", (e, on) => {
    if (defaultColorSceme == "dark")
      on = !on
    document.documentElement.classList.toggle('color-scheme-dark', on)
  })

  vars.bind("draw-sample-bg", (e, on) => {
    document.documentElement.classList.toggle('draw-sample-background', on)
  })

  let spaaSelect = vars.bind('antialias', (e, v) => {
    switch (v) {
      case 'subpixel': {
        setCSSProp('-webkit-font-smoothing', 'subpixel-antialiased')
        setCSSProp('-moz-osx-font-smoothing', 'auto')
        setCSSProp('font-smooth', 'always')
        break
      }
      case 'greyscale': {
        setCSSProp('-webkit-font-smoothing', 'antialiased')
        setCSSProp('-moz-osx-font-smoothing', 'grayscale')
        setCSSProp('font-smooth', null)
        break
      }
      default: {
        setCSSProp('-webkit-font-smoothing', 'initial')
        setCSSProp('-moz-osx-font-smoothing', 'unset')
        setCSSProp('font-smooth', null)
        break
      }
    }
  })
  const ua = navigator.userAgent.toLowerCase()
  if (ua.indexOf('win64') != -1 || ua.indexOf('win32') != -1) {
    // Can't disable on Windows
    vars.setValue('antialias', 'default')
    spaaSelect.e.disabled = true
    spaaSelect.e.parentElement.title = 'In Chrome, visit chrome:flags#lcd-text-aa to disable'
  }

  vars.bind('text-decoration', (e, v) => {
    setVendorPrefixedCSSProp('text-decoration', e.value = v)
  })

  vars.bind('text-transform', (e, v) => {
    setCSSProp('text-transform', e.value = v)
  })

  // vars.bind('variantCaps', (e, v) => {
  //   setCSSProp('font-variant-caps', e.value = v)
  // })

  // vars.bind('variantLigatures', (e, v) => {
  //   setCSSProp('font-variant-ligatures', e.value = v)
  // })

  // vars.bind('variantNumeric', (e, v) => {
  //   setCSSProp('font-variant-numeric', e.value = v)
  // })


  for (let e of $$('input.featopt')) {
    let p = e.name.replace(/^feat\:/, '').split('=')
    let name = p[0]
    let valueOn = parseInt(p[1] || '1')
    let valueOff = valueOn == 0 ? 1 : 0
    let vr = vars.bind('feat-' + name, e, (e, on) => {
      setFeature(name, on ? valueOn : valueOff, /*dontUpdateVar=*/true)
    })
    featVars.set(name, vr)
  }

  updateFeaturesStyle()

  sampleVar = vars.bind('sample', samplesSelect, (e, v) => {
    let sampleText = samples.get(v) || ''+v

    if (v == 'Repertoire') {
      repertoireControl.style.display = null
    } else {
      repertoireControl.style.display = 'none'
    }

    if (typeof sampleText == 'object' && sampleText.toHTML) {
      const html = sampleText.toHTML()
      interUISample.innerHTML   = html
      secondarySample.innerHTML = html
    } else {
      // look for directive
      // #!directive:value
      // #!directive
      sampleText = String(sampleText).replace(/^[\s\r\n\r]+|[\s\r\n\r]+$/g, '')
      let m = /(?:^|\n)#\!([\w_\-]+)(?::(.+)|)(?:\n|$)/.exec(sampleText)
      if (m) {
        // parse directive
        sampleText = (
          sampleText.substring(0, m.index) +
          sampleText.substr(m.index + m[0].length)
        )
        let directive = m[1].toLowerCase()
        // console.log('dir', m[1], '=>', m[2])
        if (directive == 'enablefeatures') {
          // #!enableFeatures:tnum,dlig
          for (let feat of m[2].toLowerCase().split(/\s*,\s*/)) {
            setFeature(feat, 1)
          }
        } else if (directive == 'notrim') {
          // #!notrim
          // noop
        } else {
          console.warn(`ignoring unknown directive ${m[0]} in sample text`)
        }
      }
      if (sampleText) {
        interUISample.innerText   = sampleText
        secondarySample.innerText = sampleText
      }
    }

    if (v == 'Repertoire') {
      requestAnimationFrame(() => {
        if (sizeVar) {
          sizeVar.refreshValue(null)
        }
      })
    }
  })

  // ESC clears keyboard focus
  document.addEventListener('keydown', ev => {
    if (ev.key == "Escape") {
      document.activeElement.blur()
    }
  }/*, {capture:true, passive:false}*/)

  requestAnimationFrame(()=>{
    $$('input').forEach(el => {
      if (el.type == "checkbox") {
        el.addEventListener("click", ev => {
          el.blur()
        })
        el.addEventListener('keypress', ev => {
          if (ev.key == "Enter") {
            el.value = !el.checked
            el.checked = !el.checked
            el.dispatchEvent(new Event("change"))
            ev.stopPropagation()
            ev.preventDefault()
          }
        },{passive:false, capture:true})
      } else {
        // RETURN on control clears keyboard focus
        el.addEventListener('keypress', ev => {
          if (ev.keyCode == 13) {
            document.activeElement.blur()
          }
        })
      }
    })
  })

  // clear keyboard focus when a select control changes
  $$('select').forEach(el => el.addEventListener('change', ev => {
    document.activeElement.blur()
    window.requestAnimationFrame(() => document.activeElement.blur())
  }))

}


main();
document.title = (
  (new Date()).toTimeString().split(':').slice(0,2).join(':') +
  ' — ' + (new Date()).toDateString()
);

})();</script>
</html>
